/**
 * Copyright 2007 Christian Mueller
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 
 * use this file except in compliance with the License. You may obtain a copy of 
 * the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by 
 * applicable law or agreed to in writing, software distributed under the License 
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
 * either express or implied. See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * Description:
 * Makes a copy of one database
 * 
 */
/**** Includes ****/
#include <stdio.h>
#include <windows.h>
#define _WINDEF_
#undef _WIN32_WINNT

#include <global.h>
#include <nsfdb.h>
#include <osmem.h>
#include <global.h>
#include "myglobal.h"
#include "w32io.h"
#include "ncopy.h"
#include "globopts.h"

#define BUFFER_SIZE   32768

void run_copy_notesdb()
{
 	LOG_DEBUG1("void run_copy_notesdb() - start");
 	
 	/** Check Database **/
 	LOG_DEBUG2("check source-database entry");
	if (opt_source_database[0]=='\0') {
		LOG_ERROR("Please define the database with option --source-database <string>");
		return;	
	}
	
	LOG_DEBUG2("check file entry");
	if (opt_dest_file[0]=='\0') {
		LOG_ERROR("Please define the destination file with option --file <string>");
		return;	
	}
	
	int ret = simpleCopy(opt_source_database,opt_dest_file);

	char message[512];
	if (ret==0) {
		sprintf(message,"Database %s successfully copied.",opt_source_database);
		LOG_INFO(message);
	} else {
		sprintf(message,"Database %s wasn't copied!",opt_source_database);
		LOG_ERROR(message);	
	}	 	
 	LOG_DEBUG1("void run_copy_notesdb() - end");
}

int simpleCopy(char* source,char* dest)
{
	LOG_DEBUG1("void simpleCopy(char* source,char* dest) - start");
	/* First open the database */
	STATUS   err = NOERROR;
	HANDLE   hDB;
	
	LOG_DEBUG3("call NSFDbOpen(source, &hDB)");
	err = NSFDbOpen(source, &hDB); 
   	if (err)
   	{
    	print_api_error (err);
    	LOG_DEBUG1("void simpleCopy(char* source,char* dest) - end");
      	return 8;
   	}
   	
   	/* Now get the full path of the source file */
   	char sourcefullpath[1024];
   	
   	LOG_DEBUG3("call NSFDbPathGet(hDB, NULL, sourcefullpath)");
   	err = NSFDbPathGet(hDB, NULL, sourcefullpath);
   	if (err)
   {
      print_api_error (err);
      LOG_DEBUG3("call NSFDbClose(hDB)");
      NSFDbClose(hDB);
      exit (EXIT_FAILURE);
   }
   
   /* Initialise the backup */
   HANDLE   BackupContext;
   DWORD    FileSizeHigh, FileSizeLow;
   LOG_DEBUG3("call NSFBackupStart");
   err = NSFBackupStart(hDB,
                        0L,
                        &BackupContext,
                        &FileSizeLow,
                        &FileSizeHigh);
                        
   if (err)
   {
      print_api_error (err);
      LOG_DEBUG3("call NSFDbClose(hDB)");
      NSFDbClose(hDB);
      LOG_DEBUG1("void simpleCopy(char* source,char* dest) - end");
      return 8;
   }
   
   /* Open the database for reading */
   int srcfd;
   LOG_DEBUG3("call SysFileOpenRead(sourcefullpath, &srcfd)");
   err = SysFileOpenRead(sourcefullpath, &srcfd);
   if (err)
   {
      print_api_error (err);
      LOG_DEBUG3("call  NSFBackupEnd(hDB, BackupContext, BACKUPEND_ABORT)");
      NSFBackupEnd(hDB, BackupContext, BACKUPEND_ABORT);
      LOG_DEBUG3("call NSFDbClose(hDB)");
      NSFDbClose(hDB);
      return 8;
   }
   
   /* Move filepointer to beginn */
   LOG_DEBUG3("call SysFileSeek(srcfd, 0, 0)");
   err = SysFileSeek(srcfd, 0, 0);
   if (err)
   {
      print_api_error (err);
      LOG_DEBUG3("call SysFileClose(srcfd)");
      SysFileClose(srcfd);
      LOG_DEBUG3("call  NSFBackupEnd(hDB, BackupContext, BACKUPEND_ABORT)");
      NSFBackupEnd(hDB, BackupContext, BACKUPEND_ABORT);
      LOG_DEBUG3("call NSFDbClose(hDB)");
      NSFDbClose(hDB);
      LOG_DEBUG1("void simpleCopy(char* source,char* dest) - end");
      return 8;
   }
   
   /* Create destination file */
   int dstfd;
   LOG_DEBUG3("call SysFileCreate(dest, &dstfd)");
   err = SysFileCreate(dest, &dstfd);
   if (err)
   {
      print_api_error (err);
      LOG_DEBUG3("call SysFileClose(srcfd);");
      SysFileClose(srcfd);
      LOG_DEBUG3("call NSFBackupEnd(hDB, BackupContext, BACKUPEND_ABORT);");
      NSFBackupEnd(hDB, BackupContext, BACKUPEND_ABORT);
      LOG_DEBUG3("call NSFDbClose(hDB)");
      NSFDbClose(hDB);
      LOG_DEBUG1("void simpleCopy(char* source,char* dest) - end");
      return 8;
   }
   
   /* Allocate Buffer for copy */
   HANDLE   hBuffer;
   LOG_DEBUG3("call OSMemAlloc(MEM_SHARE, BUFFER_SIZE, &hBuffer)");
   err = OSMemAlloc(MEM_SHARE, BUFFER_SIZE, &hBuffer);
   if (err)
   {
      print_api_error (err);
      LOG_DEBUG3("call SysFileClose(dstfd)");
      SysFileClose(dstfd);
      LOG_DEBUG3("call SysFileDelete(dest)");
      SysFileDelete(dest);
      LOG_DEBUG3("call SysFileClose(srcfd)");
      SysFileClose(srcfd);
      LOG_DEBUG3("call NSFBackupEnd(hDB, BackupContext, BACKUPEND_ABORT)");
      NSFBackupEnd(hDB, BackupContext, BACKUPEND_ABORT);
      NSFDbClose(hDB);
      LOG_DEBUG1("void simpleCopy(char* source,char* dest) - end");
      return 8;
   }
   
   /* Define the Buffer */
   char *Buffer;
   DWORD    BytesLeft;
   DWORD    BytesRead;
   DWORD    ReadCount;
   DWORD    SrcPositionLow = 0;
   DWORD    SrcPositionHigh = 0;
   Buffer = OSLock(char, hBuffer);
   
   /* Copy database file at the OS level */
   if ((MAXDWORD - FileSizeHigh) >= FileSizeLow)
   {
      BytesLeft = FileSizeHigh + FileSizeLow;
      FileSizeLow = 0;
   }
   else
   {
      BytesLeft = FileSizeHigh;
   }

   do
   {
      ReadCount = 0;
      while (BytesLeft)
      {
         BytesRead = min(BUFFER_SIZE, BytesLeft);

		 err = SysFileRead(srcfd, Buffer, BytesRead);
         if (err)
            break;

         if ((MAXDWORD - SrcPositionLow) < BytesRead)
         {
            SrcPositionHigh++;
            SrcPositionLow = (BytesRead - (MAXDWORD - SrcPositionLow)) - 1;
         }
         else
         {
            SrcPositionLow += BytesRead;
         }

         /* Occassionally tell NSF that we've made progress to cut down on
            recording overhead */
         if ((++ReadCount % 10) == 0)
            NSFBackupSetHighWaterMark(hDB,
                                      BackupContext,
                                      SrcPositionLow,
                                      SrcPositionHigh);

		 err = SysFileWrite(dstfd, Buffer, BytesRead);
         if (err)
            break;

         BytesLeft -=BytesRead;
      }

      if (FileSizeHigh > 0)
      {
         BytesLeft = MAXDWORD;
         FileSizeHigh--;
      }
      else
      {
         BytesLeft = FileSizeLow;
         FileSizeLow = 0;
      }

   } while (BytesLeft && !(err));      
   	
   /* If the copy hit a problem clean up */
   if (err)
   {
      print_api_error (err);
      LOG_DEBUG3("call OSUnlockObject(hBuffer)");
      OSUnlockObject(hBuffer);
      LOG_DEBUG3("call OSMemFree(hBuffer)");
      OSMemFree(hBuffer);
      LOG_DEBUG3("call SysFileDelete(dest)");
      SysFileDelete(dest);
      LOG_DEBUG3("call SysFileClose(srcfd)");
      SysFileClose(srcfd);
      LOG_DEBUG3("call NSFBackupEnd(hDB, BackupContext, BACKUPEND_ABORT)");
      NSFBackupEnd(hDB, BackupContext, BACKUPEND_ABORT);
      LOG_DEBUG3("call NSFDbClose(hDB)");
      NSFDbClose(hDB);
      return 8;
   }
   
   /*** Copy OS Level finished now! ***/
   NSFBackupStop(hDB, BackupContext);
   
   /* Get the change info size */
   DWORD    InfoSizeHigh;
   DWORD    InfoSizeLow;
   LOG_DEBUG3("call NSFBackupGetChangeInfoSize");
   err = NSFBackupGetChangeInfoSize(hDB,
                                        BackupContext,
                                        0,
                                        &InfoSizeLow,
                                        &InfoSizeHigh);
   if (err)
   {
      print_api_error (err);
      LOG_DEBUG3("call OSUnlockObject(hBuffer)");
      OSUnlockObject(hBuffer);
      LOG_DEBUG3("call OSMemFree(hBuffer)");
      OSMemFree(hBuffer);
      LOG_DEBUG3("call SysFileDelete(dest)");
      SysFileDelete(dest);
      LOG_DEBUG3("call SysFileClose(srcfd)");
      SysFileClose(srcfd);
      LOG_DEBUG3("call NSFBackupEnd(hDB, BackupContext, BACKUPEND_ABORT)");
      NSFBackupEnd(hDB, BackupContext, BACKUPEND_ABORT);
      LOG_DEBUG3("call NSFDbClose(hDB)");
      NSFDbClose(hDB);
      return 8;
   }

   HANDLE   ApplyInfoContext;
   /* Initiate getting the change info */
   LOG_DEBUG3("call NSFBackupStartApplyChangeInfo");
   err = NSFBackupStartApplyChangeInfo(&ApplyInfoContext,
                                           dest,
                                           0,
                                           InfoSizeLow,
                                           InfoSizeHigh);
   if (err)
   {
      print_api_error (err);
      OSUnlockObject(hBuffer);
      OSMemFree(hBuffer);
      SysFileDelete(dest);
      SysFileClose(srcfd);
      NSFBackupEnd(hDB, BackupContext, BACKUPEND_ABORT);
      NSFDbClose(hDB);
      LOG_DEBUG1("void simpleCopy(char* source,char* dest) - end");
      return 8;
   }

   /* Pull the change info and apply it to our copy */
   DWORD    FilledSize;
   do
   {
   	  LOG_DEBUG3("call NSFBackupGetNextChangeInfo");
   	  err = NSFBackupGetNextChangeInfo(hDB,
                                           BackupContext,
                                           0,
                                           Buffer,
                                           BUFFER_SIZE,
                                           &FilledSize);
      if (err)
      {
         print_api_error (err);
         LOG_DEBUG3("call NSFBackupEndApplyChangeInfo(ApplyInfoContext, APPLYEND_ABORT)");
         NSFBackupEndApplyChangeInfo(ApplyInfoContext, APPLYEND_ABORT);
         LOG_DEBUG3("call OSUnlockObject(hBuffer);");
         OSUnlockObject(hBuffer);
         LOG_DEBUG3("call OSMemFree(hBuffer);");
         OSMemFree(hBuffer);
         LOG_DEBUG3("call SysFileDelete(dest);");
         SysFileDelete(dest);
         LOG_DEBUG3("call SysFileClose(srcfd)");
         SysFileClose(srcfd);
         LOG_DEBUG3("call NSFBackupEnd(hDB, BackupContext, BACKUPEND_ABORT)");
         NSFBackupEnd(hDB, BackupContext, BACKUPEND_ABORT);
         LOG_DEBUG3("call NSFDbClose(hDB)");
         NSFDbClose(hDB);
         LOG_DEBUG1("void simpleCopy(char* source,char* dest) - end");
         return 8;
      }

	  LOG_DEBUG3("call NSFBackupApplyNextChangeInfo");
	  err = NSFBackupApplyNextChangeInfo(ApplyInfoContext,
                                             0,
                                             Buffer,
                                             FilledSize);
      if (err)
      {
         print_api_error (err);
         LOG_DEBUG3("call NSFBackupEndApplyChangeInfo");
         NSFBackupEndApplyChangeInfo(ApplyInfoContext, APPLYEND_ABORT);
         LOG_DEBUG3("call OSUnlockObject(hBuffer);");
         OSUnlockObject(hBuffer);
         LOG_DEBUG3("call OSMemFree(hBuffer)");
         OSMemFree(hBuffer);
         LOG_DEBUG3("call SysFileDelete(dest)");
         SysFileDelete(dest);
         LOG_DEBUG3("call SysFileClose(srcfd)");
         SysFileClose(srcfd);
         LOG_DEBUG3("call NSFBackupEnd(hDB, BackupContext, BACKUPEND_ABORT);");
         NSFBackupEnd(hDB, BackupContext, BACKUPEND_ABORT);
         LOG_DEBUG3("call NNSFDbClose(hDB)");
         NSFDbClose(hDB);
         LOG_DEBUG1("void simpleCopy(char* source,char* dest) - end");
         return 8;
      }

   } while (FilledSize);

   /* Done with the backup */
   LOG_DEBUG3("call NSFBackupEndApplyChangeInfo(ApplyInfoContext,0);");
   NSFBackupEndApplyChangeInfo(ApplyInfoContext,0);
   LOG_DEBUG3("call NSFBackupEnd(hDB, BackupContext, 0)");
   NSFBackupEnd(hDB, BackupContext, 0);

   /* Free up our buffer */
   LOG_DEBUG3("call OSUnlockObject(hBuffer)");
   OSUnlockObject(hBuffer);
   LOG_DEBUG3("call OSMemFree(hBuffer)");
   OSMemFree(hBuffer); 

   /* Close our source */
   LOG_DEBUG3("call SysFileClose(srcfd)");
   SysFileClose(srcfd);

   /* Close our database */
   LOG_DEBUG3("call NSFDbClose(hDB)");
   NSFDbClose(hDB);
  
   LOG_DEBUG1("void simpleCopy(char* source,char* dest) - end");
   return 0;
}
